package org.news.web.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
import org.news.web.constant.AuthorInfo;
import org.news.web.constant.CategoriesConstant;
import org.news.web.domain.vo.CategoriesTreeVo;
import org.springframework.stereotype.Service;
import org.news.web.domain.bo.CategoriesBo;
import org.news.web.domain.vo.CategoriesVo;
import org.news.web.domain.Categories;
import org.news.web.mapper.CategoriesMapper;
import org.news.web.service.ICategoriesService;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 栏目或类别Service业务层处理
 *
 * @author Gu Ci
 * @date 2024-03-23
 */
@Transactional
@RequiredArgsConstructor
@Service
public class CategoriesServiceImpl implements ICategoriesService {

    private  final ISysOssService ossService;

    private final CategoriesMapper baseMapper;




    /**
     * 查询栏目或类别
     */
    @Override
    public CategoriesVo queryById(Long categoryId){
        return baseMapper.selectVoById(categoryId);
    }


    /**
     * 查询栏目或类别列表
     */
    @Override
    public List<CategoriesVo> queryList(CategoriesBo bo) {
        LambdaQueryWrapper<Categories> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Categories> buildQueryWrapper(CategoriesBo bo) {
        LambdaQueryWrapper<Categories> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getProfilePicture()), Categories::getProfilePicture, bo.getProfilePicture());
        lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), Categories::getCategoryName, bo.getCategoryName());
        lqw.eq(StringUtils.isNotBlank(bo.getDescription()), Categories::getDescription, bo.getDescription());
        lqw.eq(bo.getParentCategoryId() != null, Categories::getParentCategoryId, bo.getParentCategoryId());
        lqw.eq(bo.getStatus() != null, Categories::getStatus, bo.getStatus());
        lqw.ge(bo.getContentCount() != null, Categories::getContentCount, bo.getContentCount());
        lqw.ge(bo.getSubscriberCount() != null, Categories::getSubscriberCount, bo.getSubscriberCount());
        return lqw;
    }

    /**
     * 新增栏目或类别
     */
    @Override
    public Boolean insertByBo(CategoriesBo bo) {
        Categories add = MapstructUtils.convert(bo, Categories.class);

        validEntityBeforeSave(add);

//        添加默认栏目的订阅数量和发布内容
        add.setContentCount(CategoriesConstant.DFcontentCount());
        add.setSubscriberCount(CategoriesConstant.DFsubscriberCount());
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setCategoryId(add.getCategoryId());
        }
        return flag;
    }

    /**
     * 修改栏目或类别
     */
    @Override
    public R updateByBo(CategoriesBo bo) {
        Categories update = MapstructUtils.convert(bo, Categories.class);
//        判断当前父节点是否是自己
        if(bo.getCategoryId() == bo.getParentCategoryId()){
            R.fail("顶级节点不能是自己 请重试！！！");
        }


//        判断是否 是否修改了状态 如果没有修改就正常修改数据
//        如果 从栏目变成类别 需要把数量和订阅变成0   如果是类别变成栏目 需要把所有子节点新闻数量加起来
//        1.查询数据
        Categories categories = baseMapper.selectById(update.getCategoryId());
//        前提是没有用户关注 也就是订阅为0
//        从栏目变成类别 需要把数量和订阅变成0
        int flag = 0;
        if(categories.getSubscriberCount() != CategoriesConstant.DFsubscriberCount()){
            flag = 1;
        }
        if(categories.getStatus() == CategoriesConstant.StatusLm() && update.getStatus() == CategoriesConstant.StatusLb()){
            if(flag == 0){
//                需要把数量和订阅变成0
                update.setContentCount(CategoriesConstant.DFcontentCount());
                update.setSubscriberCount(CategoriesConstant.DFsubscriberCount());
            }else{
//                发送失败信息
                return R.warn("该栏目有用户关注不能进行更改，建议新建！！！");
            }
        }
//       todo 前提类别没有被新闻所引用过
        if(getNewsLbList(update.getCategoryId())){
            return R.fail("该类别有新闻引用不能更改 ！！！");
        }
//       从类别变成栏目  需要把所有子节点新闻数量加起来 + 当前类别所发布的新闻数量  当前类别不可能能发布内容所以不需要
        long updateContentCounts = getChildContentCounts(categories.getCategoryId());
        if(categories.getStatus() == CategoriesConstant.StatusLb() && update.getStatus() == CategoriesConstant.StatusLm()){
            update.setContentCount(updateContentCounts);
            update.setSubscriberCount(CategoriesConstant.DFsubscriberCount());
        }

//       4. 是否修改了父节点
//        如果修改的是栏目父节点 也就是移动了该节点  当前栏目节点的所有父节点 新闻数量需要减去当前栏目的父节点数量
        if(update.getParentCategoryId() !=  categories.getCategoryId()  ){
//           //1.1获取当前节点的 新闻数量
            long newsCounts = updateContentCounts;
            //1.1 减去当前栏目之前父节点是栏目的所有新闻数量
//            获取当前栏目所有栏目父节点
            List<Long> nowLBs = new ArrayList<>();
            boolean dfByIDTO = findDFByIDTO(update.getCategoryId(), tree, nowLBs);
            //获取失败 代表没有父节点 不需要删除
            if(dfByIDTO){ //有节点
//                减少新闻数量
                boolean update1 = new LambdaUpdateChainWrapper<>(baseMapper).setSql("content_count=content_count+"+(-newsCounts)).in(Categories::getCategoryId,nowLBs).eq(Categories::getStatus,CategoriesConstant.StatusLm()).update();
                if(!update1){
                    return R.warn("删除失败");
                }
            }
            //1.2 添加上现有的父节点的所有栏目的新闻数量
//            获取现有父节点id
            List<Long> pNowLBs = new ArrayList<>();
            if(update.getParentCategoryId() != 0){
                boolean dfByIDTO1 = findDFByIDTO(update.getParentCategoryId(), tree, pNowLBs);
//                也得包括父节点
                pNowLBs.add(update.getParentCategoryId());
                if(dfByIDTO1){ //有节点
//                    添加新闻数量
                    boolean update1 = new LambdaUpdateChainWrapper<>(baseMapper).setSql("content_count=content_count+"+newsCounts).in(Categories::getCategoryId,pNowLBs).eq(Categories::getStatus,CategoriesConstant.StatusLm()).update();
                    if(!update1){
                        return R.warn("删除失败");
                    }
                }
            }
//
        }
        validEntityBeforeSave(update);
        if(baseMapper.updateById(update) > 0){
            return R.ok();
        }else{
            return R.fail();
        }
    }

    private boolean getNewsLbList(Long categoryId) {
        return false;
    }


    List<CategoriesTreeVo> tree = null;
    /**
     * 获取当前节点下的所有子节点的新闻数量和
     * @param categoryId
     * @return
     */
    public Long getChildContentCounts(Long categoryId) {
//        1.获取所有类别
        List<Categories> categories = baseMapper.selectList();
//       顶级类别
        List<Categories> root = categories.stream().filter((item) -> {
            return item.getParentCategoryId() == 0;
        }).collect(Collectors.toList());

//        2.把所有类别进行树形排序
//        todo 可以用redis进行缓存
          tree = buildTree(root,categories);
//        3.获取当前子树的所有id 通过这些id获取新闻数量

//        1.找到当前节点位置
        CategoriesTreeVo nowDF = findDFById(categoryId,tree);
//        2.把当前节点的子节点和相加
//        2.1如果当前节点是栏目的话 直接返回新闻数量值
        if(nowDF.getStatus() == CategoriesConstant.StatusLm()){
            return nowDF.getContentCount();
        }
        Long nowToChildContentCountSum = getNowToChildContentCountSum(nowDF);
        return nowToChildContentCountSum;
    }

    /**
     * 通过类别id 获取tree结构当前位置 用深度遍历
     * @param categoryId
     * @param tree
     * @return
     */
    private CategoriesTreeVo findDFById(Long categoryId, List<CategoriesTreeVo> tree) {
        if(tree == null){
            return null;
        }

        for(int i=0;i<tree.size();i++){
            if(tree.get(i).getCategoryId() == categoryId){
                return tree.get(i);
            }
            CategoriesTreeVo now = findDFById(categoryId, tree.get(i).getChildrens());
            if(now != null){
                return now;
            }
        }
        return null;
    }
    /**
     * 通过类别 用深度遍历 获取所有子节点id
     */
    private void findChild(List<CategoriesTreeVo> tree,List<Long> res){
        if(tree == null){
            return;
        }
        tree.forEach(item->{
            res.add(item.getCategoryId());
            findChild(item.getChildrens(),res);
        });
    }


    /**
     * 通过类别id 获取tree结构当前位置 返回找到的路径 也就是所有的父节点
     * @param
     * @return
     */
    private boolean findDFByIDTO(Long categoryId, List<CategoriesTreeVo> tree,List<Long> res){
        if(tree == null){
            return false;
        }
        for(int i=0;i<tree.size();i++){
            res.add(tree.get(i).getCategoryId());
            if(tree.get(i).getCategoryId() == categoryId){
                return true;
            }
            boolean flag = findDFByIDTO(categoryId, tree.get(i).getChildrens(), res);
            if(flag){
                return true;
            }
//            否则进行回溯 该节点没有找到
            res.remove(res.size()-1);
        }
        return false;
    }

    private Long getNowToChildContentCountSum(CategoriesTreeVo nowDF){
        Long sum = 0l;
//        找到当前节点是栏目直接返回值
        if(nowDF.getStatus() == CategoriesConstant.StatusLm()){
            return nowDF.getContentCount();
        }
        for(int i=0;i<nowDF.getChildrens().size();i++){
            sum += getNowToChildContentCountSum(nowDF.getChildrens().get(i))+nowDF.getContentCount();
        }

        return sum;
    }

    /**
     * 构造一棵类别树
     * @param root
     * @param categories
     * @return
     */
    private List<CategoriesTreeVo> buildTree(List<Categories> root, List<Categories> categories) {
        if(ObjectUtil.isNull(root) || ObjectUtil.isNull(categories) || root.size() == 0){
            return null;
        }
        List<CategoriesTreeVo> tree = new ArrayList<>();
        for(int i=0;i<root.size();i++){
//           1. 添加孩子节点
            tree.add(new CategoriesTreeVo(root.get(i)));
            long pid = root.get(i).getCategoryId();
//            2.收集孩子节点的子节点
            ArrayList<Categories> child = new ArrayList<>();
            for(int j=0;j<categories.size();j++){
                if(pid == categories.get(j).getParentCategoryId()){
                    child.add(categories.get(j));
                }
            }
//            3.递归调用孩子节点
            tree.get(i).setChildrens(buildTree(child,categories));
        }
        return tree;
    }


    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Categories entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除栏目或类别
     */
    @Override
    public R deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验

        }
//        对于删除的栏目类别 如果有子元素则删除失败
//        如果说有一个有子元素就整体都删除失败 没有则可以全部删除
        List<Long> tempIds = ids.stream().toList();
        Long size = baseMapper.selectCount(new QueryWrapper<Categories>().in("parent_category_id", tempIds));
        if(size >0){
            return R.warn("删除失败 有子类别或栏目！！");
        }
//        对于需要删除的类别如果有新闻引用就不能进行删除
//        todo  获取该类别新闻引用




//       查询所有要删除栏目的图片ossid 进行删除
        List<Categories> categories = baseMapper.selectList(new QueryWrapper<Categories>().in("category_id", tempIds));
        List<Long> ossIds = categories.stream().map((item) -> {
            return Long.valueOf(item.getProfilePicture());
        }).toList();
        if(ossIds == null){
            return R.fail("系统故障请联系管理员 ！！！");
        }
        Boolean aBoolean = ossService.deleteWithValidByIds(ossIds, true);
        if(!aBoolean){
            return R.fail("图片删除失败 请重试！！！");
        }


        return baseMapper.deleteBatchIds(ids) > 0?R.ok():R.fail("删除失败 请重试！！！");
    }

    @Override
    public TableDataInfo<CategoriesVo> getListCategories(AuthorInfo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Categories> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getAuthorName()), Categories::getCategoryName, bo.getAuthorName());
        lqw.eq(Categories::getStatus,CategoriesConstant.StatusLm());

        IPage<CategoriesVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public TableDataInfo<CategoriesVo> getListLbCategories(AuthorInfo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Categories> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getAuthorName()), Categories::getCategoryName, bo.getAuthorName());
        lqw.eq(Categories::getStatus,CategoriesConstant.StatusLb());
        IPage<CategoriesVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public CategoriesVo getCategorieById(Integer id) {
        if(id == null || id == 0){
            throw new ServiceException("栏目不存在");
        }
        CategoriesVo vo = baseMapper.selectVoById(id);
        return vo;
    }

    @Override
    public void addLMParantNodeValue(Long targetCategoryId, int i) {


    }

    @Override
    public List<Long> getLBListChild(Integer id) {
        ArrayList<Long> res = new ArrayList<>();

//        1.获取所有类别
        List<Categories> categories = baseMapper.selectList();
//       顶级类别
        List<Categories> root = categories.stream().filter((item) -> {
            return item.getParentCategoryId() == 0;
        }).collect(Collectors.toList());

//        2.把所有类别进行树形排序
//        todo 可以用redis进行缓存
        tree = buildTree(root,categories);


        CategoriesTreeVo dfById = findDFById(Long.valueOf(id), tree);

        if(dfById == null){
            return res;
        }
        res.add(Long.valueOf(id));
        findChild(dfById.getChildrens(),res);



        return res;
    }


    public void setImg(List<CategoriesVo> categoriesVos) {
        if(categoriesVos != null && categoriesVos.size()>0){
            Set<Long> collect = categoriesVos.stream().map(item ->{
                return Long.valueOf(item.getProfilePicture());
            }).collect(Collectors.toSet());
            List<SysOssVo> sysOssVos = ossService.listByIds(collect);
            categoriesVos.stream().forEach(item ->{
                sysOssVos.stream().forEach( img ->{
                    if(img.getOssId().longValue() == Long.valueOf(item.getProfilePicture()).longValue()){
                        item.setPictureUrl(img.getUrl());
                    }
                });
            });
        }
    }
}
